{
  "metadata" : {
    "name" : "Using Javascript and Scala to add Reactive Chart type LIVE - D3 StreamGraph",
    "user_save_timestamp" : "1970-01-01T00:00:00.000Z",
    "auto_save_timestamp" : "1970-01-01T00:00:00.000Z",
    "language_info" : {
      "name" : "scala",
      "file_extension" : "scala",
      "codemirror_mode" : "text/x-scala"
    },
    "trusted" : true,
    "customLocalRepo" : null,
    "customRepos" : null,
    "customDeps" : [ ],
    "customImports" : null,
    "customArgs" : null,
    "customSparkConf" : null
  },
  "cells" : [ {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "596A8A81F1A04C4B8D5597DC6E81F771"
    },
    "cell_type" : "code",
    "source" : "val csv = \"\"\"\nkey,value,date\nAR,0.1,01/08/13\nAR,0.15,01/09/13\nAR,0.35,01/10/13\nAR,0.38,01/11/13\nAR,0.22,01/12/13\nAR,0.16,01/13/13\nAR,0.07,01/14/13\nAR,0.02,01/15/13\nAR,0.17,01/16/13\nAR,0.33,01/17/13\nAR,0.4,01/18/13\nAR,0.32,01/19/13\nAR,0.26,01/20/13\nAR,0.35,01/21/13\nAR,0.4,01/22/13\nAR,0.32,01/23/13\nAR,0.26,01/24/13\nAR,0.22,01/25/13\nAR,0.16,01/26/13\nAR,0.22,01/27/13\nAR,0.1,01/28/13\nDJ,0.35,01/08/13\nDJ,0.36,01/09/13\nDJ,0.37,01/10/13\nDJ,0.22,01/11/13\nDJ,0.24,01/12/13\nDJ,0.26,01/13/13\nDJ,0.34,01/14/13\nDJ,0.21,01/15/13\nDJ,0.18,01/16/13\nDJ,0.45,01/17/13\nDJ,0.32,01/18/13\nDJ,0.35,01/19/13\nDJ,0.3,01/20/13\nDJ,0.28,01/21/13\nDJ,0.27,01/22/13\nDJ,0.26,01/23/13\nDJ,0.15,01/24/13\nDJ,0.3,01/25/13\nDJ,0.35,01/26/13\nDJ,0.42,01/27/13\nDJ,0.42,01/28/13\nMS,0.21,01/08/13\nMS,0.25,01/09/13\nMS,0.27,01/10/13\nMS,0.23,01/11/13\nMS,0.24,01/12/13\nMS,0.21,01/13/13\nMS,0.35,01/14/13\nMS,0.39,01/15/13\nMS,0.4,01/16/13\nMS,0.36,01/17/13\nMS,0.33,01/18/13\nMS,0.43,01/19/13\nMS,0.4,01/20/13\nMS,0.34,01/21/13\nMS,0.28,01/22/13\nMS,0.26,01/23/13\nMS,0.37,01/24/13\nMS,0.41,01/25/13\nMS,0.46,01/26/13\nMS,0.47,01/27/13\nMS,0.41,01/28/13\nRC,0.1,01/08/13\nRC,0.15,01/09/13\nRC,0.35,01/10/13\nRC,0.38,01/11/13\nRC,0.22,01/12/13\nRC,0.16,01/13/13\nRC,0.07,01/14/13\nRC,0.02,01/15/13\nRC,0.17,01/16/13\nRC,0.33,01/17/13\nRC,0.4,01/18/13\nRC,0.32,01/19/13\nRC,0.26,01/20/13\nRC,0.35,01/21/13\nRC,0.4,01/22/13\nRC,0.32,01/23/13\nRC,0.26,01/24/13\nRC,0.22,01/25/13\nRC,0.16,01/26/13\nRC,0.22,01/27/13\nRC,0.1,01/28/13\nCG,0.1,01/08/13\nCG,0.15,01/09/13\nCG,0.35,01/10/13\nCG,0.38,01/11/13\nCG,0.22,01/12/13\nCG,0.16,01/13/13\nCG,0.07,01/14/13\nCG,0.02,01/15/13\nCG,0.17,01/16/13\nCG,0.33,01/17/13\nCG,0.4,01/18/13\nCG,0.32,01/19/13\nCG,0.26,01/20/13\nCG,0.35,01/21/13\nCG,0.4,01/22/13\nCG,0.32,01/23/13\nCG,0.26,01/24/13\nCG,0.22,01/25/13\nCG,0.16,01/26/13\nCG,0.22,01/27/13\nCG,0.1,01/28/13\nRI,0.1,01/08/13\nRI,0.15,01/09/13\nRI,0.35,01/10/13\nRI,0.38,01/11/13\nRI,0.22,01/12/13\nRI,0.16,01/13/13\nRI,0.07,01/14/13\nRI,0.02,01/15/13\nRI,0.17,01/16/13\nRI,0.33,01/17/13\nRI,0.4,01/18/13\nRI,0.32,01/19/13\nRI,0.26,01/20/13\nRI,0.35,01/21/13\nRI,0.4,01/22/13\nRI,0.32,01/23/13\nRI,0.26,01/24/13\nRI,0.22,01/25/13\nRI,0.16,01/26/13\nRI,0.22,01/27/13\nRI,0.1,01/28/13\n\"\"\".trim",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "927428395F4442A18B79F283D7184CA7"
    },
    "cell_type" : "code",
    "source" : "val js = \"\"\"\nfunction streamgraph (dataO, container, options) {\n\n  var width = options.width||600\n  var height = options.height||400\n\n  var datearray = [];\n  var colorrange = [\"#045A8D\", \"#2B8CBE\", \"#74A9CF\", \"#A6BDDB\", \"#D0D1E6\", \"#F1EEF6\"];\n  var strokecolor = colorrange[0];\n\n  var format = d3.time.format(options.date_format);\n\n  var margin = {top: 20, right: 40, bottom: 30, left: 30};\n\n  var tooltip = d3.select(container)\n      .append(\"div\")\n      .attr(\"class\", \"remove\")\n      .style(\"position\", \"absolute\")\n      .style(\"z-index\", \"20\")\n      .style(\"visibility\", \"hidden\")\n      .style(\"top\", (10 + margin.top)+\"px\")\n      .style(\"left\", (125 + margin.left)+\"px\");\n\n\n\n  var x = d3.time.scale()\n      .range([0, width]);\n\n  var y = d3.scale.linear()\n      .range([height-10, 0]);\n\n  var z = d3.scale.ordinal()\n      .range(colorrange);\n\n  var xAxis = d3.svg.axis()\n      .scale(x)\n      .orient(\"bottom\")\n      .ticks(d3.time.weeks);\n\n  var yAxis = d3.svg.axis()\n      .scale(y);\n\n  var yAxisr = d3.svg.axis()\n      .scale(y);\n\n  var stack = d3.layout.stack()\n      .offset(\"silhouette\")\n      .values(function(d) { return d.values; })\n      .x(function(d) { return d[options.date]; })\n      .y(function(d) { return d[options.value]; });\n\n  var nest = d3.nest()\n      .key(function(d) { return d[options.category]; });\n\n  var area = d3.svg.area()\n      .interpolate(\"cardinal\")\n      .x(function(d) { return x(d[options.date]); })\n      .y0(function(d) { return y(d.y0); })\n      .y1(function(d) { return y(d.y0 + d.y); });\n\n  var svg = d3.select(container).append(\"svg\")\n      .attr(\"width\", width + margin.left + margin.right)\n      .attr(\"height\", height + margin.top + margin.bottom)\n    .append(\"g\")\n      .attr(\"transform\", \"translate(\" + margin.left + \",\" + margin.top + \")\");\n    var graph = function(data) {\n      data.forEach(function(d) {\n        d[options.date] = format.parse(d[options.date]);\n        d[options.value] = +d[options.value];\n      });\n\n      var layers = stack(nest.entries(data));\n\n      x.domain(d3.extent(data, function(d) { return d[options.date]; }));\n      y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);\n\n      svg.selectAll(\".layer\")\n          .data(layers)\n        .enter().append(\"path\")\n          .attr(\"class\", \"layer\")\n          .attr(\"d\", function(d) { return area(d.values); })\n          .style(\"fill\", function(d, i) { return z(i); });\n\n\n      svg.append(\"g\")\n          .attr(\"class\", \"x axis\")\n          .attr(\"transform\", \"translate(0,\" + height + \")\")\n          .call(xAxis);\n\n      svg.append(\"g\")\n          .attr(\"class\", \"y axis right\")\n          .attr(\"transform\", \"translate(\" + width + \", 0)\")\n          .call(yAxis.orient(\"right\"));\n\n      svg.append(\"g\")\n          .attr(\"class\", \"y axis left\")\n          .call(yAxis.orient(\"left\"));\n\n      svg.selectAll(\".layer\")\n          .attr(\"opacity\", 1)\n          .on(\"mouseover\", function(d, i) {\n            svg.selectAll(\".layer\").transition()\n            .duration(250)\n            .attr(\"opacity\", function(d, j) {\n              return j != i ? 0.6 : 1;\n          })})\n\n          .on(\"mousemove\", function(d, i) {\n            mousex = d3.mouse(this);\n            mousex = mousex[0];\n            var invertedx = x.invert(mousex);\n            invertedx = invertedx.getMonth() + invertedx.getDate();\n            var selected = (d.values);\n            for (var k = 0; k < selected.length; k++) {\n              datearray[k] = selected[k][options.date]\n              datearray[k] = datearray[k].getMonth() + datearray[k].getDate();\n            }\n\n            mousedate = datearray.indexOf(invertedx);\n            window.pro = d.values[mousedate][options.value];\n\n            d3.select(this)\n            .classed(\"hover\", true)\n            .attr(\"stroke\", strokecolor)\n            .attr(\"stroke-width\", \"0.5px\"), \n            tooltip.html( \"<p>\" + d.key + \"<br>\" + window.pro + \"</p>\" ).style(\"visibility\", \"visible\");\n            \n          })\n          .on(\"mouseout\", function(d, i) {\n           svg.selectAll(\".layer\")\n            .transition()\n            .duration(250)\n            .attr(\"opacity\", \"1\");\n            d3.select(this)\n            .classed(\"hover\", false)\n            .attr(\"stroke-width\", \"0px\"), tooltip.html( \"<p>\" + d.key + \"<br>\" + window.pro + \"</p>\" ).style(\"visibility\", \"hidden\");\n          });\n    };\n\n  var vertical = d3.select(container)\n      .append(\"div\")\n      .attr(\"class\", \"remove vertical\")\n      .style(\"position\", \"absolute\")\n      .style(\"z-index\", \"19\")\n      .style(\"width\", \"1px\")\n      .style(\"height\", (margin.height - margin.bottom - margin.top)+\"px\")\n      .style(\"top\", margin.bottom+\"px\")\n      .style(\"bottom\", margin.bottom+\"px\")\n      .style(\"left\", margin.left+\"px\")\n      .style(\"background\", \"#fff\");\n\n  d3.select(container)\n      .on(\"mousemove\", function(){  \n         mousex = d3.mouse(this);\n         mousex = mousex[0] + 15;\n         vertical.style(\"left\", (100 + mousex) + \"px\" )})\n      .on(\"mouseover\", function(){  \n         mousex = d3.mouse(this);\n         mousex = mousex[0] + 15;\n         vertical.style(\"left\", (100 + mousex) + \"px\")});\n      \n  graph(this.dataInit);\n\n  dataO.subscribe( function(data) { \n    data.forEach(function(d) {\n      d[options.date] = format.parse(d[options.date]);\n      d[options.value] = +d[options.value];\n    });\n\n    var layers = stack(nest.entries(data));\n\n    x.domain(d3.extent(data, function(d) { return d[options.date]; }));\n    y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);\n\n    svg.selectAll(\"path.layer\")\n        .data(layers)\n        .transition()\n          .duration(2500)\n          .attr(\"d\", function(d) { return area(d.values); })\n          .style(\"fill\", function(d, i) { return z(i); });\n        \n    svg.selectAll(\"g.x.axis\")\n        .transition()\n          .duration(2500)\n          .ease(\"linear\")\n          .call(xAxis);\n\n    svg.selectAll(\"g.y.axis.right\")\n        .transition()\n          .duration(2500)\n          .ease(\"linear\")\n          .call(yAxis.orient(\"right\"));\n\n    svg.selectAll(\"g.y.axis.left\")\n        .transition()\n          .duration(2500)\n          .ease(\"linear\")\n          .call(yAxis.orient(\"left\"));\n  });\n\n}\n\"\"\".trim",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "A4C3A8D3DDF94291892A216518327EAB"
    },
    "cell_type" : "code",
    "source" : "case class Data(k:String, v:String, d:String)",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "09A1613AD7B14BF5B4713B1CE114A57E"
    },
    "cell_type" : "code",
    "source" : "val data = csv.split(\"\\n\").map(_.split(\",\").toList).toList.tail.map {\n  case List(k, v, d) => Data(k, v, d)\n}\n()",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "57D3B5CBCB014850848ED875EB283064"
    },
    "cell_type" : "code",
    "source" : "import notebook.front.widgets._\nimport notebook.front.widgets.magic._\nimport notebook.front.widgets.magic.Implicits._\nimport notebook.front.widgets.magic.SamplerImplicits._\n\ncase class StreamgraphChart[C:ToPoints:Sampler](\n  originalData:C,\n  maxPoints:Int = 25,\n  category:String,\n  date:String,\n  value:String,\n  dateFormat:String\n\n) extends charts.Chart[C](originalData, maxPoints) {\n\n  //override val sizes:(Int, Int)=(600, 400),\n\n  def mToSeq(t:MagicRenderPoint):Seq[(String, Any)] = t.data.toSeq\n\n\n  override val snippets = List(s\"\"\"|{\n                                   |  \"f\": $js, \n                                   |  \"o\": {\n                                   |    \"category\": \"$category\",\n                                   |    \"value\": \"$value\",\n                                   |    \"date\": \"$date\",\n                                   |    \"date_format\": \"$dateFormat\"\n                                   |  }\n                                   |}\n                                  \"\"\".stripMargin)\n  \n    override val scripts = List(notebook.front.Script(\"consoleDir\", play.api.libs.json.Json.obj()))\n  \n}",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "A37426154E9846518883D99C49801647"
    },
    "cell_type" : "code",
    "source" : "val s = StreamgraphChart(data, category=\"k\", value=\"v\", date=\"d\", dateFormat=\"%m/%d/%y\", maxPoints=10000)\ns",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "88D887D295BC44189E89E67CDC9AE67A"
    },
    "cell_type" : "code",
    "source" : "def createData(month:String) = data.map(x => x.copy(v=x.v+scala.util.Random.nextInt(50),\n                                                 d = month+x.d.drop(2)))",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "8FCDE3B387004B33BAC6F21D7349B01A"
    },
    "cell_type" : "code",
    "source" : "s.addAndApply(createData(\"02\"))",
    "outputs" : [ ]
  }, {
    "metadata" : {
      "trusted" : true,
      "input_collapsed" : false,
      "collapsed" : false,
      "id" : "AB2B64BB52F048A58C05B9E0D2EAAAC5"
    },
    "cell_type" : "code",
    "source" : "s.addAndApply(createData(\"02\") ::: createData(\"03\"))",
    "outputs" : [ ]
  } ],
  "nbformat" : 4
}